#include <iostream>
#include <sstream>
#include <fstream>
#include <string>
#include <cstring>
#include <cstdio>
#include <ctime>
#include <cstdlib>

#include <stdio.h>
#include <stdlib.h>

#include "../bstree.h"
#include "../register_pack.h"

using namespace bstree;

class KeyKeyVal : public Register{
	private:
		std::string key;
	public:
		KeyKeyVal(std::string key){
			this->key = key;
			(this->key).resize(20,'\0');
	
		}

		KeyKeyVal(const KeyKeyVal& k2){
			this->key = k2.key;
			(this->key).resize(20,'\0');
		}
		
		std::string getKey()const{ return this->key;  }

		Register* duplicate()const{
			return (new KeyKeyVal(*this));
		}

		void setFields(const Register& b2){
			const KeyKeyVal& temp = dynamic_cast<const KeyKeyVal&>(b2);
			this->key = temp.key;
		}

		unsigned int getSize()const {
			return (key.size());
		};

		bool operator<(const Register& b2)const{
			const KeyKeyVal& temp = dynamic_cast<const KeyKeyVal&>(b2);
			return (this->key<temp.key);
		}

		std::ostream& toOstream(std::ostream& out)const{
			out << this->key;
			return out;
		}
};


class KeyVal : public Register{
	private:
		std::string key;
		unsigned int idKey;
		unsigned int offset;
		std::string lexico;
	public:
		KeyVal(const std::string& key,unsigned int idKey){
			this->key   = key;
			this->idKey = idKey;
			this->offset=0;
			lexico="";
		}
		KeyVal(const KeyVal& k2){
			this->key   = k2.getKey();
			this->idKey = k2.getIdKey();
			this->offset = k2.getOffset();
			this->lexico = k2.getLexico();
		}

		std::string getKey()const{ return this->key;  }
		unsigned int getIdKey()const{ return this->idKey; }
		unsigned int getOffset()const{ return this->offset; }
		std::string getLexico()const{ return this->lexico; }
		
		void setOffset(unsigned int offset){ this->offset=offset; }
		void setLexico(std::string lexico){ this->lexico=lexico; }
		
		Register* duplicate()const{
			return (new KeyVal(*this));
		}

		void setFields(const Register& b2){
			const KeyVal& temp = dynamic_cast<const KeyVal&>(b2);
			this->key = temp.getKey();
			this->idKey = temp.getIdKey();
			this->offset = temp.getOffset();
			this->lexico = temp.getLexico();
		}

		unsigned int getSize()const {
			return (sizeof(unsigned int)*4+key.size()+lexico.size());
		};

		bool operator<(const Register& b2)const{
			const KeyVal& temp = dynamic_cast<const KeyVal&>(b2);
			return (this->key<temp.getKey());
		}

		Register* getKeyRegister()const{
			KeyKeyVal* reg = new KeyKeyVal(this->key);
			return reg;
		}

		std::ostream& toOstream(std::ostream& out)const{
			out << "\t\t Key   : " << this->key << std::endl;
			out << "\t\t IdKey : " << this->idKey << std::endl;
			out << "\t\t Offset : " << this->offset << std::endl;
			out << "\t\t Lexico : " << this->lexico << std::endl;
			return out;
		}
};


class KeyKeyValFactory : public RegisterFactory{
	public:
		KeyKeyValFactory(){}
		~KeyKeyValFactory(){}

		RegisterFactory* duplicate()const{ return new KeyKeyValFactory(); }

		Register* operator ()(char* data)const{
			char* temp = data;
			char* keyTemp;
			keyTemp=new char[21];
			memcpy(keyTemp,temp,20);
			keyTemp[20]='\0';
			std::string finalKey(keyTemp);
			delete[] keyTemp;
			return (new KeyKeyVal(finalKey));
		}
		
		char* operator ()(const Register& reg,char* data)const{
			const KeyKeyVal& kkvreg = dynamic_cast<const KeyKeyVal&>(reg);
			char* temp = data;
			const char* tempKey = kkvreg.getKey().c_str();
			memcpy(temp,tempKey,kkvreg.getKey().size());
			temp+=kkvreg.getKey().size();
			return temp;
		}
};

class KeyValFactory : public RegisterFactory{
	public:
		KeyValFactory(){}
		~KeyValFactory(){}

		RegisterFactory* duplicate()const{ return new KeyValFactory(); }

		Register* operator ()(char* data)const{
			char* temp = data;
			unsigned int tempIdKey;
			unsigned int keyLength;
			unsigned int offset;
			unsigned int lexicoLength;
			char* keyTemp;
			char* lexicoTemp;

			memcpy(&keyLength,temp,sizeof(unsigned int));
			temp += sizeof(unsigned int);
			
			keyTemp = new char[keyLength+1];
			memset(keyTemp,0,keyLength+1);
			memcpy(keyTemp,temp,keyLength);
			temp += keyLength;
			keyTemp[keyLength]='\0';

			memcpy(&tempIdKey,temp,sizeof(unsigned int));
			temp += sizeof(unsigned int);

			memcpy(&offset,temp,sizeof(unsigned int));
			temp += sizeof(unsigned int);

			memcpy(&lexicoLength,temp,sizeof(unsigned int));
			temp += sizeof(unsigned int);
			
			lexicoTemp = new char[lexicoLength+1];
			memset(lexicoTemp,0,lexicoLength+1);
			memcpy(lexicoTemp,temp,lexicoLength);
			temp += lexicoLength;
			lexicoTemp[lexicoLength]='\0';

			KeyVal* kv = new KeyVal(std::string(keyTemp),tempIdKey);
			kv->setOffset(offset);
			kv->setLexico(std::string(lexicoTemp));

			delete[] keyTemp;
			delete[] lexicoTemp;
			return (kv);
		}
		
		char* operator ()(const Register& reg,char* data)const{
			const KeyVal& kvreg = dynamic_cast<const KeyVal&>(reg);
			char* temp = data;
			const char* tempKey;
			const char* tempLexico;
			unsigned int tempIdKey = kvreg.getIdKey();
			unsigned int keyLength = kvreg.getKey().size();
			unsigned int tempOffset = kvreg.getOffset();
			unsigned int lexicoLength = kvreg.getLexico().size();

			memcpy(temp,&keyLength,sizeof(unsigned int));
			temp+=sizeof(unsigned int);

			tempKey = kvreg.getKey().c_str();
			memcpy(temp,tempKey,keyLength);
			temp+=keyLength;

			memcpy(temp,&tempIdKey,sizeof(unsigned int));
			temp+=sizeof(unsigned int);

			memcpy(temp,&tempOffset,sizeof(unsigned int));
			temp+=sizeof(unsigned int);

			memcpy(temp,&lexicoLength,sizeof(unsigned int));
			temp+=sizeof(unsigned int);

			tempLexico = kvreg.getLexico().c_str();
			memcpy(temp,tempLexico,lexicoLength);
			temp+=lexicoLength;

			return temp;
		}
};

bool generateRandomRegister(KeyVal** reg, unsigned int value);
bool generateSpecificRegister(KeyVal** reg, unsigned int value);

int main(int argc,char** argv){
	KeyValFactory kvf;
	KeyKeyValFactory kkvf;	

	

	BSTree* bstree = new BSTree(256,kvf,kkvf,"./data/bpptree.dat");

/*	KeyVal kv1("clave1",1);
	kv1.setOffset(43);
	kv1.setLexico(std::string("ESTELasdfasdfsadfEXIA1"));
	bstree->insert(kv1);
	std::cout << *bstree;

	KeyVal kv2("clave2",2);
	kv2.setLexico(std::string("ESTELdfshfqwtqwetdjEXIg2"));
	bstree->insert(kv2);
	
	KeyVal kv3("clave3",3);
	kv3.setLexico(std::string("ESTELhkreygjlfEXIg3"));
	bstree->insert(kv3);

	KeyVal kv4("clave4",4);
	kv4.setLexico(std::string("ESTELEsdfuuertgsfdhXIg4"));
	bstree->insert(kv4);
	
	std::cout << *bstree;

	KeyVal kv5("clave5",5);
	kv5.setOffset(43);
	kv5.setLexico(std::string("ESTELEhkhgrettrelhjXICBA2dsfg"));
	bstree->insert(kv5);

	KeyVal kv6("clave6",6);
	kv6.setLexico(std::string("ESTELEXICergretrydvdsgBA2dsfg"));
	bstree->insert(kv6);


	KeyVal kv7("clave07",7);
	kv7.setLexico(std::string("ESTELEXICasdfergdvdsgBA2dsfg"));
	bstree->insert(kv7);

	std::cout << *bstree;
	
	KeyVal kv8("clave08",8);
	kv8.setLexico(std::string("ESTELEXICwerergdvdsgBA2dsfg"));
	bstree->insert(kv8);

	std::cout << *bstree;
	std::cout<<"-----------------------------------------------------\n";

	KeyVal kv9("clave09",9);
	kv9.setLexico(std::string("ESTELEXICweredvdsgBA2dsfg"));
	bstree->insert(kv9);

	std::cout << *bstree;
	std::cout<<"-----------------------------------------------------\n";*/

/*	KeyVal kv15("clave15",9);
	kv15.setLexico(std::string("ESdfgdfgrfdhfdhrtuttueutehvbdhdfgmgfg"));
	bstree->insert(kv15);

	std::cout << *bstree;
*/	
	std::ofstream log_out;
	std::string nomArch="./data/log.txt";
	log_out.open(nomArch.c_str(), std::ios::out);

	std::ofstream log_out2;
	nomArch="./data/log2.txt";
	log_out2.open(nomArch.c_str(), std::ios::out);

	srand(time(0));
	for(int h=0;h<460;h++){
		std::cout<<h<<"\n";
		KeyVal* kk;
		//generateRandomRegister(&kk, 12);
		//generateSpecificRegister(&kk, h);
		//kk->toOstream(std::cout);
		bstree->insert(*kk);
		delete kk;
	}
	std::cout<<"-----------------------------------------------------\n";
	log_out << *bstree;

	KeyVal* kk;
	//generateRandomRegister(&kk, 12);
	generateSpecificRegister(&kk, 460);
	//kk->toOstream(std::cout);
	bstree->insert(*kk);
	delete kk;

	std::cout<<"-----------------------------------------------------\n";
	log_out2 << *bstree;

	log_out.close();
	log_out2.close();

/*	KeyVal kv10("clave10",10);
	bstree->insert(kv10);
	KeyVal kv11("clave11",11);
	bstree->insert(kv11);
	KeyVal kv12("clave12",12);
	bstree->insert(kv12);
	
	std::cout << *bstree;

	KeyVal kv9("clave9",9);
	bstree->search(kv9);
	kv9.toOstream(std::cout);

	//bstree->remove(kv3);
	//bstree->remove(kv3);
	//bstree->remove(kv11);

	//std::cout << *bstree;

	//bstree->remove(kv7);

	//std::cout << *bstree;
*/
	/*bstree->remove(kv12);
	bstree->remove(kv10);
	bstree->remove(kv9);
	bstree->remove(kv1);
	bstree->remove(kv5);
	bstree->remove(kv8);
	bstree->remove(kv4);
	bstree->remove(kv2);
	bstree->remove(kv6);*/
/*	//bstree->remove(kv11);*/

	/*KeyVal kv("clave",5);
	std::string aux = "false";
	if(bstree->insert(kv)==true) 
		aux="true";	
	std::cout<<aux<<std::endl;*/

/*
	unsigned int keys = 60;
	for(unsigned int c=0;c<keys;c++){
		unsigned int key = c;
		std::string=
		KeyVal kv("clave",key);
		bstree->insert(kv);
	}
*/	
	//std::cout << *bstree;
	
	delete bstree;

/*
	btree = new BTree(128,new KeyValFactory(),"./data/btree.dat");	
	for(unsigned int c=0;c<keys;c++){
		temp += itos(c);
		temp += ".dump";
	
		unsigned int key = keysArray[rand()%keys];
		kins2 >> key;
		//krmv >> key;
		KeyVal kv("clave",key);
		//if(key!=45)
			btree->remove(kv);
		//else
			//btree->remove(kv);
		//step << "Key " << key << " has been erased" << std::endl << std::endl << *btree;
		kdmprmv << key << std::endl;
	}
	rmv << *btree;

	delete btree;
	delete[] keysArray;

	btree = new BTree(128,new KeyValFactory(),"./data/btree.dat");	
	kins2.clear();
	kins2.seekg( 0 , std::fstream::beg );
	for(unsigned int c=0;c<keys;c++){
		unsigned int key = c;
		kins2 >> key;
		KeyVal kv("clave",key);
		btree->insert(kv);
	}
	
	delete btree;*/
	return 0;
}

std::string generateRandomWord(unsigned int length){
	std::string ret;
	int random_integer;
	for(unsigned int i=0; i<length; i++){
		random_integer = (rand()%25)+97; 
		ret+=(char)random_integer;
	}
	return ret;
}

bool generateRandomRegister(KeyVal** reg, unsigned int value){
	unsigned int random_lenght = (rand()%5)+4;
	std::string str=generateRandomWord(random_lenght);
	*reg=new KeyVal(str,value);
	random_lenght = (rand()%15)+15; 
	(*reg)->setLexico(generateRandomWord(random_lenght));
	random_lenght = (rand()%10000); 
	(*reg)->setOffset(random_lenght);
	return true;
}


bool generateSpecificRegister(KeyVal** reg, unsigned int value){

	char aux[5];
	sprintf(aux,"%d",value); 
	std::string str(aux);
	*reg=new KeyVal(str,value);
	unsigned int random_lenght = (rand()%15)+15; 
	//(*reg)->setLexico(generateRandomWord(random_lenght));
	(*reg)->setLexico(generateRandomWord(30));
	random_lenght = (rand()%10000); 
	(*reg)->setOffset(random_lenght);
	return true;
}


std::string itos(int n){
	std::ostringstream temp;
	temp << n;
	return temp.str();
}
